package com.teacher.game.framework.animation;

/**
 * 补间动画插值器
 * 
 * 提供各种动画插值函数，用于计算动画过程中的中间值。
 * 支持线性插值、缓入缓出、弹跳效果等多种动画曲线。
 * 
 * 使用示例：
 * <pre>
 * // 创建缓入缓出插值器
 * Tween tween = new Tween(0, 100, 1.0f, Tween.EASE_IN_OUT);
 * 
 * // 获取50%进度时的值
 * float value = tween.getValue(0.5f); // 约25（非线性）
 * </pre>
 * 
 * @version 1.0
 * @since 2025-09-12
 */
public class Tween {
    
    // 插值函数类型常量
    public static final int LINEAR = 0;      // 线性插值
    public static final int EASE_IN = 1;     // 缓入
    public static final int EASE_OUT = 2;    // 缓出
    public static final int EASE_IN_OUT = 3; // 缓入缓出
    public static final int BOUNCE = 4;      // 弹跳效果
    public static final int ELASTIC = 5;     // 弹性效果
    public static final int OVERSHOOT = 6;   // 超调效果
    
    private float startValue;    // 起始值
    private float endValue;      // 结束值
    private float duration;      // 持续时间（秒）
    private int interpolationType; // 插值类型
    
    /**
     * 构造补间动画插值器
     * 
     * @param startValue 起始值
     * @param endValue 结束值  
     * @param duration 持续时间（秒）
     * @param interpolationType 插值类型
     */
    public Tween(float startValue, float endValue, float duration, int interpolationType) {
        this.startValue = startValue;
        this.endValue = endValue;
        this.duration = duration;
        this.interpolationType = interpolationType;
    }
    
    /**
     * 根据进度获取插值结果
     * 
     * @param progress 动画进度 (0.0 ~ 1.0)
     * @return 插值后的数值
     */
    public float getValue(float progress) {
        // 确保进度在有效范围内
        progress = Math.max(0f, Math.min(1f, progress));
        
        // 应用插值函数
        float interpolatedProgress = applyInterpolation(progress);
        
        // 计算最终值
        return startValue + (endValue - startValue) * interpolatedProgress;
    }
    
    /**
     * 根据时间获取插值结果
     * 
     * @param currentTime 当前时间（秒）
     * @return 插值后的数值
     */
    public float getValueAtTime(float currentTime) {
        float progress = currentTime / duration;
        return getValue(progress);
    }
    
    /**
     * 应用插值函数
     * 
     * @param t 原始进度 (0.0 ~ 1.0)
     * @return 插值后的进度 (0.0 ~ 1.0)
     */
    private float applyInterpolation(float t) {
        switch (interpolationType) {
            case LINEAR:
                return t;
                
            case EASE_IN:
                return t * t;
                
            case EASE_OUT:
                return 1 - (1 - t) * (1 - t);
                
            case EASE_IN_OUT:
                return t < 0.5f ? 2 * t * t : 1 - (float) Math.pow(-2 * t + 2, 2) / 2;
                
            case BOUNCE:
                return bounceOut(t);
                
            case ELASTIC:
                return elasticOut(t);
                
            case OVERSHOOT:
                float tension = 1.5f;
                return 1 + tension * (float) Math.pow(t - 1, 3) + (float) Math.pow(t - 1, 2);
                
            default:
                return t;
        }
    }
    
    /**
     * 弹跳插值函数
     */
    private float bounceOut(float t) {
        float n1 = 7.5625f;
        float d1 = 2.75f;
        
        if (t < 1 / d1) {
            return n1 * t * t;
        } else if (t < 2 / d1) {
            return n1 * (t -= 1.5f / d1) * t + 0.75f;
        } else if (t < 2.5 / d1) {
            return n1 * (t -= 2.25f / d1) * t + 0.9375f;
        } else {
            return n1 * (t -= 2.625f / d1) * t + 0.984375f;
        }
    }
    
    /**
     * 弹性插值函数
     */
    private float elasticOut(float t) {
        float c4 = (2 * (float) Math.PI) / 3;
        
        return t == 0 ? 0 : t == 1 ? 1 : 
               (float) (Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1);
    }
    
    // Getter方法
    public float getStartValue() { return startValue; }
    public float getEndValue() { return endValue; }
    public float getDuration() { return duration; }
    public int getInterpolationType() { return interpolationType; }
    
    /**
     * 检查动画是否已完成
     * 
     * @param currentTime 当前时间（秒）
     * @return 是否已完成
     */
    public boolean isCompleted(float currentTime) {
        return currentTime >= duration;
    }
}